# -*-coding: utf-8 -*-
"""
作者：TianyiFan
日期：2023-年08-月16日
用途：RMP 模型类+MP模型类
"""
from gurobipy import *
import numpy as np
import para as params
import time


class plan_poor:
    # 类的共有成员---直接覆盖
    dual_epsilo_i, dual_yita_k, dual_theta_tg, dual_pai_tn, dual_miu_t = {}, {}, {}, {}, {}  # MP问题约束的对偶值
    def __init__(self):
        self.A_pi_k = np.zeros([params.V+2, params.total_plan, params.K1], dtype=int)  # 是否预留某个k
        self.B_pig = np.zeros([params.V+2, params.total_plan, params.G], dtype=int)    # 某个的block 中预留的数量，一组block
        self.D_pin = np.zeros([params.V+2, params.total_plan, params.pair_N], dtype=int)  # 某个的neighbor_pair 中预留的数量，一对pair
        self.Fpi_l = np.zeros([params.V+2, params.total_plan], dtype=int)  # loading过程中的转运量---绝对值
        self.Fpi_u = np.zeros([params.V+2,params.total_plan], dtype=int)   # unloading过程中的转运量--绝对值
        self.Cost_pi = np.zeros([params.V+2,params.total_plan], dtype=float)  # 某个计划的成本


class RMP(object):
    def __init__(self):
        self.model = Model("RMP_model")
        self.model.setParam("OutputFlag", params.outflag_RMP)
        self.model.setParam("Method", 2)
        self.lambda_pi = {}
        self.model_common = model_func()

    def operate_model(self, base):
        '''
        基于给定的参数，遍历每艘船的计划池子的长度，运行对应的RMP模型
        :param base:
        :return:
        '''
        # print(f"***************校验当前计划-{params.set_vessel[i]} 每艘船的cost**********")
        # check_cost = 0
        # for i in range(1, params.V + 1):
        #     print("RMP 中 params.set_vessel[i]={} vessel-{} 的cost ={}".format(params.set_vessel[i] - 1, i, base.Cost_pi[params.set_vessel[i] - 1, i]))
        #     check_cost += base.Cost_pi[params.set_vessel[i] - 1, i]
        # print(f"总成本= {check_cost:.2f} 换算为小时={check_cost / 3600:.2f}")
        time1 = time.time()
        # region step1:初始化模型的决策变量和目标函数
        for i in range(params.V+2):
            for p in range(params.set_vessel[i]):
                self.lambda_pi[i,p] =  self.model.addVar(0, 1, vtype=GRB.CONTINUOUS, name='lambda_{}_{}'.format(i,p))
        obj = LinExpr(0)

        for i in range(1, params.V + 1):
            for p in range(params.set_vessel[i]):
                obj.addTerms(base.Cost_pi[i,p], self.lambda_pi[i,p])
        self.model.setObjective(obj, GRB.MINIMIZE)  # 初始化目标函数最小化
        # endregion

        # region step2:更新约束条件
        # 约束1：每艘船每个计划只能选一个
        constr_1 = {}
        for i in range(1, params.V + 1):
            constr_1[i] = self.model.addConstr(quicksum(self.lambda_pi[i, p] for p in range(params.set_vessel[i])) == 1, name='Constr1_{}'.format(i))
        # 约束2：每个 subblock 只能被一艘船占用
        constr_2 = {}
        for k in range(params.K1):
            constr_2[k] = self.model.addConstr(
                quicksum(self.lambda_pi[i,p] * base.A_pi_k[i,p,k] for i in range(1, params.V + 1) for p in range(params.set_vessel[i])) <= 1, name='Constr2_{}'.format(k))
        # 约束3：每个block内同一个step内，最多有3个subblock在工作
        constr_3 = {}
        for t in range(params.T):
            for g in range(params.G):
                constr_3[t, g] = self.model.addConstr(quicksum(
                    self.lambda_pi[i, p] * params.l_it[i, t] * base.B_pig[i,p, g] for i in range(1, params.V + 1) for p in range(params.set_vessel[i])) <= 3,
                                                      name='Constr3_{}_{}_{}'.format(t, g,params.block_set[g]))
        # 约束4：每个neighbor_pair，同一个step内，只能有1个在工作
        # print("RMP model 中 pair_N = {}".format(params.pair_N))
        constr_4 = {}
        for t in range(params.T):
            for n in range(params.pair_N):
                constr_4[t, n] = self.model.addConstr(
                    quicksum(self.lambda_pi[i, p] * base.D_pin[i,p, n] * params.l_it[i, t] for i in range(1, params.V + 1) for p in range(params.set_vessel[i])) <= 1,
                    name='Constr4_{}_{}_{}'.format(t, n, params.neighbor_pair[n]))
        # 约束5：单位时间内，穿过孔洞的集装箱数量有限
        constr_5 = {}
        for t in range(params.T):
            constr_5[t] = self.model.addConstr(
                quicksum(
                    self.lambda_pi[i, p] *params.l_it[i,t]* ((base.Fpi_l[i,p] + base.Fpi_u[i,p]) / params.handle_i[i])
                    for i in range(1, params.V + 1) for p in range(params.set_vessel[i]) ) <= params.C,
                name='Constr5_%d' % t)
        # endregion

        # step3:模型更新与求解
        self.model.update()
        # self.model.write("RMP_model.lp")
        self.model.optimize()

        time2 = time.time()
        # step4:模型输出
        if self.model.getAttr(GRB.attr.Status) == 2:
            print("输出_lambda_pi--------------------------")
            for i in range(1,params.V+1):
                 for p in range(params.set_vessel[i]):
                    if self.lambda_pi[i, p].x > 0:
                        self.model_common.plan_info(i,self.lambda_pi[i, p].x , p, base)
            print("第{0}次迭代 最优解={2:.2f}  RMP求解时间={1:.2f}".format(params.iter_count, time2 - time1, self.model.ObjVal))

            # 更新SP对偶变量值
            for i in range(1, params.V + 1):
                base.dual_epsilo_i[i] = constr_1[i].Pi
            for k in range(params.K1):
                base.dual_yita_k[k] = constr_2[k].Pi
            for t in range(params.T):
                for g in range(params.G):
                    base.dual_theta_tg[t, g] = constr_3[t, g].Pi
                for n in range(params.pair_N):
                    base.dual_pai_tn[t, n] = constr_4[t, n].Pi
                base.dual_miu_t[t] = constr_5[t].Pi
        elif self.model.getAttr(GRB.attr.Status) == 3:
            print('Optimization was stopped with status =  %d' % self.model.getAttr(GRB.attr.Status))
            self.model.computeIIS()
            for c in self.model.getConstrs():
                if c.IISConstr:
                    print('%s' % c.constrName)
            raise EOFError("RMP model 无解")
        elif self.model.getAttr(GRB.attr.Status) == 5:
            print("无界解")


class PP(object):
    def __init__(self, vessel_index,mode=1):
        self.model = Model("PP_model_vessel_{0}".format(vessel_index))
        self.model.setParam("OutputFlag", params.outflag_PP)
        self.model.setParam("Method", 3)
        self.vessel_index = vessel_index
        # 参数
        self.maxbound = 9999
        self.minbound = -9999
        # region 定义决策变量
        self.gamma_k = {}  # 对应RMP中的参数A_pik
        self.rho_g = {}    # 对应RMP中的参数B_pig
        self.chi_n = {}    # 对应RMP中的参数B_pin
        self.alpha_hu, self.alpha_hl = {}, {}
        self.beta_ku, self.beta_kl = {}, {}
        self.sigma_u, self.sigma_l = {}, {}
        self.cost_plan = {}
        self.abs_ui1 = self.model.addVar(0, self.maxbound, vtype=GRB.INTEGER, name="abs_ui1")  # 对应RMP中的参数F_pi_u
        self.abs_li1 = self.model.addVar(0, self.maxbound, vtype=GRB.INTEGER, name="abs_li1")  # 对应RMP中的参数F_pi_l
        self.repeat_plan = []


        for g in range(params.G):
            name1 = 'rho_{0}'.format(g)
            self.rho_g[g] = self.model.addVar(0, 5, vtype=GRB.INTEGER, name=name1)
        for n in range(params.pair_N):
            name1 = 'chi_{0}'.format(n)
            self.chi_n[n] = self.model.addVar(0, 2, vtype=GRB.INTEGER, name=name1)
        for h in range(params.H):
            name1 = "alpha_{0}_u".format(h)
            name2 = "alpha_{0}_l".format(h)
            self.alpha_hu[h] = self.model.addVar(0, params.c_ui[vessel_index], vtype=GRB.INTEGER, name=name1)
            self.alpha_hl[h] = self.model.addVar(0, params.c_li[vessel_index], vtype=GRB.INTEGER, name=name2)
        for k in range(params.K1):
            name1 = "beta_{0}_u".format(k)
            name2 = "beta_{0}_l".format(k)
            self.beta_ku[k] = self.model.addVar(0, params.z, vtype=GRB.INTEGER, name=name1)
            self.beta_kl[k] = self.model.addVar(0, params.z, vtype=GRB.INTEGER, name=name2)
        for k in range(params.K1):
            name = "gamma_{0}".format(k)
            self.gamma_k[k] = self.model.addVar(0, 1, vtype=GRB.BINARY, name=name)

        name3 = "cost_{0}".format(vessel_index)
        self.cost_plan = self.model.addVar(0, 999999, vtype=GRB.INTEGER, name=name3)
        name4 = "sigma_l"
        name5 = "sigma_u"
        self.sigma_l = self.model.addVar(0, GRB.INFINITY, vtype=GRB.INTEGER, name=name4)
        self.sigma_u = self.model.addVar(0, GRB.INFINITY, vtype=GRB.INTEGER, name=name5)
        # endregion

    def operate_model(self, base):
        # 先清空原来的约束
        # print("原来模型的约束数量：{}".format(self.model.NumConstrs))
        # all_constraints = self.model.getConstrs()
        # self.model.remove(all_constraints)
        # print("清空后模型的约束数量：{}".format(self.model.NumConstrs))

        # region step1:定义约束条件
        # 已经为reduce_cost为负的解，不同同时成立
        # if len(self.repeat_plan)>0:
        #     for plan in self.repeat_plan:
        #         number = len(plan)
        #         # print("vessel-{}  需要排除的解={}".format(self.vessel_index, plan))
        #         self.model.addConstr(quicksum(self.gamma_k[k] for k in plan) <= number-1,name ="repeated_constrain")
        # 约束1：卸货量泊位侧守恒
        self.model.addConstr(self.alpha_hu[0] + self.alpha_hu[1] == params.c_ui[self.vessel_index], name='constrain_1')
        # 约束2：卸货量堆场侧守恒
        expr2 = LinExpr(0)
        for k in range(params.K1):
            expr2.add(self.beta_ku[k])
        self.model.addConstr(expr2 == params.c_ui[self.vessel_index], name='constrain_2')
        # 约束3：装货量泊位侧守恒
        self.model.addConstr(self.alpha_hl[0] + self.alpha_hl[1] == params.c_li[self.vessel_index], name='constrain_3')
        # 约束4：装货量堆场侧守恒
        expr4 = quicksum(self.beta_kl[k] for k in range(params.K1))
        self.model.addConstr(expr4 == params.c_li[self.vessel_index], name='constrain_4')
        # 约束5：装货量堆场侧每层总量上界
        expr5 = quicksum(self.beta_kl[k] + self.beta_ku[k] for k in range(params.K0))
        self.model.addConstr(expr5 <= params.z * params.r_ih[self.vessel_index][0], name='constrain_5_{0}'.format(0))  # 下层的堆场
        expr6 = quicksum(self.beta_kl[k] + self.beta_ku[k] for k in range(params.K0, params.K1))
        self.model.addConstr(expr6 <= params.z * params.r_ih[self.vessel_index][1], name='constrain_5_{0}'.format(1))  # 上层的堆场
        # 约束6：装货量堆场侧单堆场上界
        for k in range(params.K1):
            self.model.addConstr(self.beta_kl[k] + self.beta_ku[k] <= params.z * self.gamma_k[k], name='constrain_6_{0}'.format(k))
        # 约束7：堆场数量上界
        expr7 = quicksum(self.gamma_k[k] for k in range(params.K0))
        self.model.addConstr(expr7 <= params.r_ih[self.vessel_index][0], name='constrain_7_0')
        expr8 = quicksum(self.gamma_k[k] for k in range(params.K0, params.K1))
        self.model.addConstr(expr8 <= params.r_ih[self.vessel_index][1], name='constrain_7_1')
        # 约束8：一个block内，最多预留其中其中3个
        for g in range(params.G):
            expr = LinExpr(0)
            for k in params.block_set[g]:
                expr.add(self.gamma_k[k])
            self.model.addConstr(expr <= 3, name="Constrain8_{0}".format(g))
        # 约束9：不能预留相邻的堆场
        for pair in range(params.pair_N):
            expr = LinExpr(0)
            for k in params.neighbor_pair[pair]:
                expr.add(self.gamma_k[k])
            self.model.addConstr(expr <= 1, name="Constrain9_pair-{}-{}-{}".format(pair, params.neighbor_pair[pair][0], params.neighbor_pair[pair][1]))
        # 约束8：目标函数中max(a,b)线性化
        for h in range(params.H):
            self.model.addConstr(self.sigma_u >= self.alpha_hu[h])
            self.model.addConstr(self.sigma_l >= self.alpha_hl[h])
        # 约束9：计划的成本计算公式：
        self.model.addConstr(self.cost_plan ==
                             params.time_p * (self.sigma_u + self.sigma_l)
                             + params.time_u * (self.abs_ui1 + self.abs_li1)
                             + quicksum(params.t_lki[k][self.vessel_index] * self.beta_kl[k] for k in range(params.K1))
                             + quicksum(params.t_uki[k][self.vessel_index] * self.beta_ku[k] for k in range(params.K1)))
        # 约束10：堆场转运数量的绝对值线性化
        sum_li1 = self.model.addVar(self.minbound, self.maxbound, vtype=GRB.INTEGER, name="sum_li1")
        sum_ui1 = self.model.addVar(self.minbound, self.maxbound, vtype=GRB.INTEGER, name="sum_ui1")
        expr3 = quicksum(self.beta_kl[k] for k in range(params.K0))
        expr4 = quicksum(self.beta_ku[k] for k in range(params.K0))
        self.model.addConstr(sum_li1 == self.alpha_hl[0] - expr3, name="Constrain_sum_l")
        self.model.addConstr(sum_ui1 == self.alpha_hu[0] - expr4, name="Constrain_sum_u")
        self.model.addGenConstrAbs(self.abs_li1, sum_li1, name="abs_constrain_l")
        self.model.addGenConstrAbs(self.abs_ui1, sum_ui1, name="abs_constrain_u")
        # 约束11：ganma_k和g的关联
        for g in range(params.G):
            self.model.addConstr(self.rho_g[g] == quicksum(self.gamma_k[k] for k in params.block_set[g]), name="constrain11_{}".format(g))
        # 约束12：ganma_k和n的关联
        for n in range(params.pair_N):
            self.model.addConstr(self.chi_n[n] == quicksum(self.gamma_k[k] for k in params.neighbor_pair[n]),name="constrain12_{}".format(n))
        # 约束13：单个船的转运量在单位时间内，也要小于总数
        for t in range(params.T):
            expr = LinExpr(0)
            if params.l_it[self.vessel_index, t] == 1:
                expr.addTerms(1 / params.handle_i[self.vessel_index], self.abs_ui1)
                expr.addTerms(1 / params.handle_i[self.vessel_index], self.abs_li1)
            self.model.addConstr(expr <= params.C, name="Constrain13_{0}".format(t))


        # endregion
        # step2:目标函数
        cost1 = quicksum(base.dual_yita_k[k] * self.gamma_k[k] for k in range(params.K1))
        cost2 = quicksum(params.l_it[self.vessel_index][t] * base.dual_theta_tg[t, g] * self.rho_g[g] for t in range(params.T) for g in range(params.G))
        cost3 = quicksum(params.l_it[self.vessel_index][t] * base.dual_pai_tn[t, n] * self.chi_n[n] for t in range(params.T) for n in range(params.pair_N))
        cost444 = self.abs_li1 + self.abs_ui1  # 船i的loading和unloading过程中的跨层转运量
        cost4 = 0
        for t in range(params.T):
            cost4 += base.dual_miu_t[t] / params.handle_i[self.vessel_index]
        cost4 = cost4 * cost444
        obj1 = self.cost_plan - base.dual_epsilo_i[self.vessel_index]
        obj2 = cost1 + cost2 + cost3 + cost4
        obj = obj1 - obj2
        self.model.setObjective(obj, GRB.MINIMIZE)  # 目标函数最小化

        # step3:模型更新与求解
        self.model.update()
        # self.model.write("PP_model_{0}.lp".format(self.vessel_index)
        self.model.optimize()

        # step4: 模型输出
        if self.model.getAttr(GRB.attr.Status) == 2:
            print("PP_vessel-{0} 的 obj={1:.2f}   cost_i={2}".format(self.vessel_index, self.model.ObjVal, self.cost_plan.x))
            print("输出gamma_k和beta_k的结果-------------")
            for k in range(params.K1):
                if self.gamma_k[k].x > 0.5:
                    print('gamma_{0:2} = 1  '.format(k), end="  ")
                    print('beta_l_{0:2} = {1:.2f}  distance={2}'.format(k, self.beta_kl[k].x,params.t_lki[k,self.vessel_index]), end="  ")
                    print('beta_u_{0:2} = {1:.2f}  distance={2}'.format(k, self.beta_ku[k].x,params.t_uki[k,self.vessel_index]))
            # print("输出alpha_h 的结果-------------")
            # print("alpha_l_{} = {}  alpha_l_{} = {}   c_li={}".format(0, self.alpha_hl[0].x, 1, self.alpha_hl[1].x, params.c_li[self.vessel_index]))
            # print("alpha_u_{} = {}  alpha_u_{} = {}   c_ui={}".format(0, self.alpha_hu[0].x, 1, self.alpha_hu[1].x, params.c_ui[self.vessel_index]))
            # print("输出异层转运的结果-------------")
            # print("unloading 过程中的转运量={}    loading过程中的转运量={:.2f}".format(self.abs_ui1.x, self.abs_li1.x))
        elif self.model.getAttr(GRB.attr.Status) == 3:
            print("无解")
            print('Optimization was stopped with status =  %d' % self.model.getAttr(GRB.attr.Status))
            self.model.computeIIS()
            for c in self.model.getConstrs():
                if c.IISConstr:
                    print('%s' % c.constrName)
            raise StopIteration("PP_vessel-{0} model 无解！！！".format(self.vessel_index))

    def updata_rmp_plan(self, base):
        '''
        基于当前的计划号索引params.set_vessel[i],更新base中对应计划下的列
        :param params.set_vessel[i]:
        :param base:
        :return:
        '''
        # 更新A_pi_k 、 B_pig
        v = self.vessel_index
        for g in range(params.G):
            block_count = 0
            for k in params.block_set[g]:
                if self.gamma_k[k].x > 0.5:
                    base.A_pi_k[self.vessel_index,params.set_vessel[v], k] = 1
                    block_count += 1
            base.B_pig[self.vessel_index,params.set_vessel[v], g] = block_count
        # 更新 D_pin
        for pair in range(params.pair_N):
            pair_count = 0
            for k in params.neighbor_pair[pair]:
                if self.gamma_k[k].x > 0.5:
                    pair_count += 1
            base.D_pin[self.vessel_index,params.set_vessel[v], pair] = pair_count
        # 更新Fpi_l，Fpi_u
        base.Fpi_u[self.vessel_index,params.set_vessel[v]] = self.abs_ui1.x
        base.Fpi_l[self.vessel_index,params.set_vessel[v]] = self.abs_li1.x
        # 更新Cost_pi
        cost_trans = 0
        for k in range(params.K1):
            cost_trans += self.beta_ku[k].x * params.t_uki[k, self.vessel_index] + self.beta_kl[k].x * params.t_lki[k, self.vessel_index]
            base.Cost_pi[self.vessel_index,params.set_vessel[v]] = \
                params.time_p * (self.sigma_u.x + self.sigma_l.x) + params.time_u * (self.abs_li1.x + self.abs_ui1.x) + cost_trans
        # print('vessel-{}  plan={}  cost_pi={}'.format(self.vessel_index, params.set_vessel[i], base.Cost_pi[params.set_vessel[i], self.vessel_index]))

    def tabulist_solution(self,base):
        '''
        判断前params.set_vessel[v]-1个计划，是否和当前ppmodel求出来的解存在重复的
        :param base:
        :return:
        '''
        flag_A = True
        flag_Fpi_u= True
        flag_Fpi_l = True
        v = self.vessel_index
        for plan_index in range(params.set_vessel[v]):
            # 判断A_pki是否都一样
            count = 0
            for k in range(params.K1):
                if self.gamma_k[k].x != base.A_pi_k[self.vessel_index,plan_index, k]:
                    flag_A = False
                    break
                else:
                    count += 1
            if count == params.K1:
                flag_A = True

            if self.abs_li1.x != base.Fpi_l[self.vessel_index,plan_index]:
                flag_Fpi_l = False
            else:
                flag_Fpi_l = True

            if self.abs_ui1.x != base.Fpi_u[self.vessel_index,plan_index]:
                flag_Fpi_u = False
            else:
                flag_Fpi_u = True
            if flag_A and flag_Fpi_l and flag_Fpi_u:
                print("vessel-{}  plan={} 存在和之前一模一样的列".format(self.vessel_index,plan_index))
                break
        print("vessel-{} plan_index={} flag_A ={} flag_Fpi_l={} flage_Fpi_u={}".format(self.vessel_index,params.set_vessel[v],flag_A,flag_Fpi_l,flag_Fpi_u))
        return flag_A and flag_Fpi_l and flag_Fpi_u

class MP(object):
    def __init__(self):
        self.model = Model("MP_model")
        self.model.setParam("OutputFlag", params.outflag_RMP)
        self.model.setParam("Method", 3)
        self.lambda_pi = {}
        self.model_common = model_func()
        print("CG迭代结束，现在开始求解原来的MP模型-----------------------------------")

    def operate_model(self, base):
        # region step1:初始化模型的决策变量和目标函数
        for i in range(params.V + 2):
            for p in range(params.set_vessel[i]):
                self.lambda_pi[i, p] = self.model.addVar(0, 1, vtype=GRB.BINARY, name='lambda_{}_{}'.format(i, p))
        obj = LinExpr(0)
        for i in range(1, params.V + 1):
            for p in range(params.set_vessel[i]):
                obj.addTerms(base.Cost_pi[i, p], self.lambda_pi[i, p])
        self.model.setObjective(obj, GRB.MINIMIZE)  # 初始化目标函数最小化
        # endregion

        # region step2:更新约束条件
        # 约束1：每艘船每个计划只能选一个
        constr_1 = {}
        for i in range(1, params.V + 1):
            constr_1[i] = self.model.addConstr(quicksum(self.lambda_pi[i, p] for p in range(params.set_vessel[i])) == 1, name='Constr1_{}'.format(i))
        # 约束2：每个 subblock 只能被一艘船占用
        constr_2 = {}
        for k in range(params.K1):
            constr_2[k] = self.model.addConstr(
                quicksum(self.lambda_pi[i, p] * base.A_pi_k[i, p, k] for i in range(1, params.V + 1) for p in range(params.set_vessel[i])) <= 1, name='Constr2_{}'.format(k))
        # 约束3：每个block内同一个step内，最多有3个subblock在工作
        constr_3 = {}
        for t in range(params.T):
            for g in range(params.G):
                constr_3[t, g] = self.model.addConstr(quicksum(
                    self.lambda_pi[i, p] * params.l_it[i, t] * base.B_pig[i, p, g] for i in range(1, params.V + 1) for p in range(params.set_vessel[i])) <= 3,
                                                      name='Constr3_{}_{}'.format(t, g))
        # 约束4：每个neighbor_pair，同一个step内，只能有1个在工作
        # print("RMP model 中 pair_N = {}".format(params.pair_N))
        constr_4 = {}
        for t in range(params.T):
            for n in range(params.pair_N):
                constr_4[t, n] = self.model.addConstr(
                    quicksum(self.lambda_pi[i, p] * base.D_pin[i, p, n] * params.l_it[i, t] for i in range(1, params.V + 1) for p in range(params.set_vessel[i])) <= 1,
                    name='Constr4_{}_{}_{}'.format(t, n, params.neighbor_pair[n]))
        # 约束5：单位时间内，穿过孔洞的集装箱数量有限
        constr_5 = {}
        for t in range(params.T):
            constr_5[t] = self.model.addConstr(
                quicksum(
                    self.lambda_pi[i, p] * params.l_it[i, t] * ((base.Fpi_l[i, p] + base.Fpi_u[i, p]) / params.handle_i[i])
                    for i in range(1, params.V + 1) for p in range(params.set_vessel[i])) <= params.C,
                name='Constr5_%d' % t)
        # endregion

        # step3:模型更新与求解
        self.model.update()
        self.model.optimize()

        # step4:模型输出
        if self.model.getAttr(GRB.attr.Status) == 2:
            obj_value = self.model.ObjVal
            print("最优解={0}".format(self.model.ObjVal))
            print("输出_lambda_pi--------------------------")
            for i in range(1, params.V + 1):
                for p in range(params.set_vessel[i]):
                    if self.lambda_pi[i, p].x > 0:
                        self.model_common.plan_info(i, self.lambda_pi[i, p].x,p, base)
        else:
            obj_value = 999999
            raise EOFError("MP问题无解！！")
        return obj_value

class generate_plan_rh(object):
    def __init__(self):
        self.gamma_ik = np.zeros([params.V + 2, params.K1], dtype=int)
        self.alpha_ih_u = np.zeros([params.V + 2, params.H], dtype=int)
        self.alpha_ih_l = np.zeros([params.V + 2, params.H], dtype=int)
        self.beta_lik = np.zeros([params.V + 2, params.K1], dtype=int)
        self.beta_uik = np.zeros([params.V + 2, params.K1], dtype=int)
        self.obj_rule = 0

    def rolling_horizon(self):
        # todo 得到不同船的决策序列，进行滚动决策，从而生成不同的计划，代入到模型中进行求解
        pass


class model_func(object):
    def plan_info(self, vessel_index,lambda_pi, plan_index, base):
        '''
        基于给定的计划池，根据船v选择的计划p，输出对应计划的堆场分配
        :param vessel_index:
        :param plan_index:
        :param base:
        :return:
        '''
        print("vessel-{} lambda = {:.2f} 选择计划-{} cost={:.2f} 预留subblock = ".format(vessel_index,lambda_pi ,plan_index,base.Cost_pi[vessel_index,plan_index]), end="")
        for k in range(params.K1):
            if base.A_pi_k[vessel_index, plan_index, k] == 1:
                print(f"{k} ", end="")
        print()
        # for n in range(params.pair_N):
        #     if base.D_pin[plan_index, vessel_index, n] > 0:
        #         print("计划-{} 中第 {} 个pair-{} 预留的堆场数 = {}".format(plan_index,n,params.neighbor_pair[n],base.D_pin[plan_index,vessel_index,n]))

